package com.hengpeng.itfin.domain.imprest.alipay;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;

import com.hengpeng.common.exception.ServiceResult;
import com.hengpeng.common.util.StringHelper;
import com.hengpeng.common.util.WebClientUtil;
import com.hengpeng.itfin.domain.imprest.AbstractImprestProcessor;
import com.hengpeng.itfin.domain.imprest.alipay.util.MD5Signature;
import com.hengpeng.itfin.domain.imprest.alipay.util.ParameterUtil;
import com.hengpeng.itfin.exception.PaymentException;
import com.hengpeng.itfin.service.ServiceFactory;
import com.hengpeng.itfinbase.constants.PaymentConstants;
import com.hengpeng.itfinbase.message.partner.NotifyRequestMsg;
import com.hengpeng.itfinbase.message.partner.NotifyResponseMsg;
import com.hengpeng.itfinbase.message.partner.PayRequestMsg;
import com.hengpeng.itfinbase.message.partner.PayResponseMsg;
import com.hengpeng.itfinbase.persist.Payment;
import com.hengpeng.itfinbase.persist.Payment.PayStatus;
import com.hengpeng.itfinbase.persist.PaymentProvider;

/**
 * <pre>
 * 支付宝wap支付网关接口
 * </pre>
 * @author shanguogang
 * @version 1.0, 2015-8-17
 */
public class AlipayWapImprestProcessor extends AbstractImprestProcessor
{

	private final static Log logger = LogFactory.getLog(AlipayWapImprestProcessor.class);

	private static final String ALIPAYWAP_CONFIG_V = "v";

	private static final String ALIPAYWAP_CONFIG_SECID = "sec_id";

	private static final String ALIPAYWAP_CONFIG_AUTHSERVICE = "auth_service";

	private static final String ALIPAYWAP_CONFIG_FORMAT = "format";

	private static final String ALIPAYWAP_CONFIG_PRIVATEID = "privateKeyId";

	private static final String ALIPAYWAP_CONFIG_SERVICE = "service";

	private static final String ALIPAYWAP_CONFIG_SUBJECT = "subject";

	private static final String ALIPAYWAP_CONFIG_SELLERACCOUNTNAME = "seller_account_name";

	private String v;

	private String sec_id;

	private String auth_service;

	private String format;

	private String privateKeyId;

	private String service;

	private String subject;

	private String seller_account_name;

	public AlipayWapImprestProcessor(ServiceFactory serviceFactory, PaymentProvider provider)
	{
		super(serviceFactory, provider);
		v = provider.getExtConfigs().get(ALIPAYWAP_CONFIG_V);
		sec_id = provider.getExtConfigs().get(ALIPAYWAP_CONFIG_SECID);
		auth_service = provider.getExtConfigs().get(ALIPAYWAP_CONFIG_AUTHSERVICE);
		format = provider.getExtConfigs().get(ALIPAYWAP_CONFIG_FORMAT);
		privateKeyId = provider.getExtConfigs().get(ALIPAYWAP_CONFIG_PRIVATEID);
		service = provider.getExtConfigs().get(ALIPAYWAP_CONFIG_SERVICE);
		subject = provider.getExtConfigs().get(ALIPAYWAP_CONFIG_SUBJECT);
		seller_account_name = provider.getExtConfigs().get(ALIPAYWAP_CONFIG_SELLERACCOUNTNAME);
	}

	@Override
	public void payRequest(PayRequestMsg request, PayResponseMsg response)
	{
		// TODO Auto-generated method stub
		Map<String, String> postValues = new HashMap<String, String>();
		if (request.getRetUrl() != null && !"".equals(request.getRetUrl().trim()))
		{
			page_return_url = request.getRetUrl();
		}
		if (request.getBgRetUrl() != null && !"".equals(request.getBgRetUrl().trim()))
		{
			service_notify_url = request.getBgRetUrl();
		}
		String orderId = request.getOrdId();
		/**
		 * 获取sign签名信息和req_data请求信息
		 */
		doDeposit(postValues, orderId, request.getTransAmt(), page_return_url, service_notify_url);

		postValues.put("v", v);
		postValues.put("sec_id", sec_id);
		postValues.put("service", auth_service);
		postValues.put("partner", this.provider.getMerchantId());
		postValues.put("format", format);
		// 交易正常
		String actionString = this.provider.getPayUrl();
		String htmlString = WebClientUtil.autoSubmitHtmlForm(PaymentConstants.PORTAL_RESPONSE_DEFAULT_ENCODING, "网上支付",
				actionString, postValues, "GET");
		logger.info("生成的支付表单内容为：\n" + htmlString);
		response.setOrdId(orderId);
		// response.setProviderId(this.provider.getId());
		response.setMerchantId(this.provider.getMerchantId());
		// response.setOrderDate(DateTimeUtil.formatToStr(new Date(), DateTimeUtil.DATE_FORMAT_YMD));
		response.setRespCode(PaymentConstants.PAYMENT_TRANSMESSAGE_ERRCODE_SUCCESS);
		response.setRespDesc(PaymentConstants.PAYMENT_TRANSMESSAGE_ERRMSG_SUCCESS);
		response.setOrderFormContent(htmlString);
	}

	@Override
	public void payNotify(NotifyRequestMsg request, NotifyResponseMsg response)
	{

		Map<String, String> parameter = request.getParameter();
		String page_result = parameter.get("result");
		if (page_result != null && page_result.trim() != "")
		{
			if ("success".equals(page_result))
			{
				response.setMerchantId(this.provider.getMerchantId());
				// response.setMoney(Double.valueOf(money));
				response.setOrdId(parameter.get("out_trade_no"));
				return;
			}
			else
			{
				response.setRespCode(PaymentConstants.PAYMENT_TRANSMESSAGE_ERRCODE_OTHER);
				response.setRespDesc("处理失败，若确认已付款，请联系客服冲正！");
				response.setMerchantId(this.provider.getMerchantId());
				// response.setMoney(Double.valueOf(money));
				response.setOrdId(parameter.get("out_trade_no"));
				return;
			}
		}

		StringBuffer verifyData = new StringBuffer();
		verifyData.append("service=");
		verifyData.append(parameter.get("service"));
		verifyData.append("&v=");
		verifyData.append(parameter.get("v"));
		verifyData.append("&sec_id=");
		verifyData.append(parameter.get("sec_id"));
		verifyData.append("&notify_data=");
		verifyData.append(parameter.get("notify_data"));
		logger.info("verifyData=[" + verifyData.toString() + "],sign=[" + parameter.get("sign") + "]"
				+ "privateKeyId=[" + privateKeyId + "]");
		boolean verified = verify(verifyData.toString(), parameter.get("sign"), privateKeyId);// 验证签名
		if (verified)
		{
			try
			{// 成功
				Document document = DocumentHelper.parseText(parameter.get("notify_data"));
				Element root = document.getRootElement();
				String trade_status = root.elementText("trade_status");
				String money = root.elementText("total_fee");
				String orderNo = root.elementText("out_trade_no");
				Double doubleMoney = Double.parseDouble(money) * 100;
				// 交易成功 trade_status=TRADE_FINISHED
				if ("TRADE_FINISHED".equals(trade_status) || "TRADE_SUCCESS".equals(trade_status))
				{
					ServiceResult<Payment> result = serviceFactory.getPayService().dealPayResult(
							request.getProviderId(), orderNo, doubleMoney.longValue(), PayStatus.SUCCESS, "支付成功");
					if (!result.getIsSuccess())
					{
						response.setRespCode(result.getCode());
						response.setRespDesc(result.getMessage());
					}
					else
					{
						response.setMerchantId(this.provider.getMerchantId());
						response.setTransAmt(money);
						response.setOrdId(orderNo);
					}
				}
				else
				{
					logger.error("充值失败");
					response.setRespCode(PaymentConstants.PAYMENT_TRANSMESSAGE_ERRCODE_OTHER);
					response.setRespDesc("充值失败！");
					serviceFactory.getPayService().dealPayResult(response.getProviderId(), response.getOrdId(),
							doubleMoney.longValue(), PayStatus.FAILED, response.getRespDesc());
				}
			}
			catch (DocumentException e)
			{
				response.setRespCode(PaymentConstants.PAYMENT_TRANSMESSAGE_ERRCODE_OTHER);
				response.setRespDesc("处理失败，若确认已付款，请联系客服冲正！");
			}
		}
		else
		{// 验签失败
			logger.error("alipay wap 验签失败");
			response.setRespCode(PaymentConstants.PAYMENT_TRANSMESSAGE_ERRCODE_DIGESTERROR);
			response.setRespDesc(PaymentConstants.PAYMENT_TRANSMESSAGE_ERRMSG_DIGESTERROR);
		}
	}

	/**
	 * 获取签名和请求数据
	 * @param postValues
	 * @param sequenceNo
	 * @param money
	 * @param page_return_url
	 * @param notify_url
	 */
	private void doDeposit(Map<String, String> postValues, String sequenceNo, String money, String page_return_url,
			String notify_url)
	{
		// 1.创建订单,获取token
		StringBuffer reqData = new StringBuffer();
		reqData.append("<direct_trade_create_req><subject>");
		reqData.append(subject);
		reqData.append("</subject><out_trade_no>");
		reqData.append(sequenceNo);
		reqData.append("</out_trade_no><total_fee>");
		reqData.append(money);
		reqData.append("</total_fee><seller_account_name>");
		reqData.append(seller_account_name);
		reqData.append("</seller_account_name><call_back_url>");
		reqData.append(page_return_url);
		reqData.append("</call_back_url><notify_url>");
		reqData.append(notify_url);
		reqData.append("</notify_url></direct_trade_create_req>");

		Map<String, String> requestParams = new HashMap<String, String>();
		requestParams.put("req_data", reqData.toString());
		requestParams.put("req_id", System.currentTimeMillis() + "");
		requestParams.put("service", service);
		requestParams.put("sec_id", sec_id);
		requestParams.put("partner", this.provider.getMerchantId());
		requestParams.put("format", format);
		requestParams.put("v", v);

		// 签名
		String sign = sign(requestParams, privateKeyId);
		requestParams.put("sign", sign);
		Map<String, String> resResult = new HashMap<String, String>();
		try
		{// 发起创建订单请求
			resResult = send(requestParams, this.provider.getPayUrl(), sec_id);
		}
		catch (Exception e)
		{
			e.printStackTrace();
			logger.info("创建订单异常，请重试");
		}
		String businessResult = "";
		if ("true".equals(resResult.get("isSuccess")))
		{
			businessResult = resResult.get("businessResult");
		}
		else
		{
			logger.info("创建订单失败，" + resResult.get("errorMessage"));
		}
		String token = null;
		try
		{// 解析返回xml，获取request_token
			Document document = DocumentHelper.parseText(businessResult);
			Element root = document.getRootElement();
			token = root.elementText("request_token");
		}
		catch (DocumentException e)
		{
			e.printStackTrace();
			logger.info("创建订单数据解析异常");
		}
		if (StringHelper.isEmpty(token))
		{
			logger.info("获取token出错，稍后再试");
		}

		Map<String, String> authParams = new HashMap<String, String>();
		authParams.put("req_data", "<auth_and_execute_req><request_token>" + token
				+ "</request_token></auth_and_execute_req>");
		authParams.put("service", service);
		authParams.put("sec_id", sec_id);
		authParams.put("partner", this.provider.getMerchantId());
		authParams.put("format", format);
		authParams.put("v", v);
		authParams.put("service", auth_service);
		String authSign = sign(authParams, privateKeyId);// 对调用授权请求数据签名

		postValues.put("req_data", authParams.get("req_data"));
		postValues.put("sign", authSign);
	}

	/**
	 * 向支付宝发送创建订单请求
	 * @param requestParams
	 * @param serverUrl
	 * @param sec_id
	 * @return
	 */
	private Map<String, String> send(Map<String, String> requestParams, String serverUrl, String sec_id)
			throws Exception
	{
		String response = "";
		try
		{
			serverUrl = serverUrl + "?";
			URL url = new URL(serverUrl);
			HttpURLConnection conn = (HttpURLConnection) url.openConnection();
			conn.setRequestMethod("POST");
			conn.setDoOutput(true);
			conn.connect();

			String params = ParameterUtil.mapToUrl(requestParams);
			conn.getOutputStream().write(params.getBytes());

			InputStream is = conn.getInputStream();
			BufferedReader in = new BufferedReader(new InputStreamReader(is));
			StringBuffer buffer = new StringBuffer();
			String line = "";
			while ((line = in.readLine()) != null)
			{
				buffer.append(line);
			}
			response = URLDecoder.decode(buffer.toString(), "utf-8");
			conn.disconnect();

			if (StringHelper.isEmpty(response))
			{
				throw new PaymentException("创建订单时，返回信息为null");
			}

		}
		catch (Exception e)
		{
			throw e;
		}
		return praseResult(response, sec_id);
	}

	/**
	 * 解析支付宝返回信息
	 * @param response
	 * @param sec_id
	 * @return
	 * @throws Exception
	 */
	private Map<String, String> praseResult(String response, String sec_id) throws Exception
	{
		Map<String, String> resultMap = new HashMap<String, String>();
		// 调用成功
		HashMap<String, String> resMap = new HashMap<String, String>();
		String v = ParameterUtil.getParameter(response, "v");
		String service = ParameterUtil.getParameter(response, "service");
		String partner = ParameterUtil.getParameter(response, "partner");
		String sign = ParameterUtil.getParameter(response, "sign");
		String reqId = ParameterUtil.getParameter(response, "req_id");
		resMap.put("v", v);
		resMap.put("service", service);
		resMap.put("partner", partner);
		resMap.put("sec_id", sec_id);
		resMap.put("req_id", reqId);
		String businessResult = "";
		if (response.contains("<err>"))
		{
			resultMap.put("isSuccess", "false");
			businessResult = ParameterUtil.getParameter(response, "res_error");
			// 转换错误信息
			try
			{
				Document document = DocumentHelper.parseText(businessResult);
				Element root = document.getRootElement();
				resultMap.put("errorMessage",
						"errorCode: " + root.elementText("code") + "  detail:" + root.elementText("detail"));
			}
			catch (Exception e)
			{
				throw e;
			}
			resMap.put("res_error", businessResult);
		}
		else
		{
			businessResult = ParameterUtil.getParameter(response, "res_data");
			resultMap.put("isSuccess", "true");
			resultMap.put("businessResult", businessResult);
			resMap.put("res_data", businessResult);
		}
		// 获取待签名数据
		String verifyData = ParameterUtil.getSignData(resMap);
		// 对待签名数据使用支付宝公钥验签名
		boolean verified = verify(verifyData, sign, privateKeyId);
		if (!verified)
		{
			throw new Exception("验证签名失败");
		}
		return resultMap;
	}

	/**
	 * 验签
	 * @param verifyData 待签名数据
	 * @param sign 返回的签名
	 * @param key MD5 key
	 * @return
	 */
	private boolean verify(String verifyData, String sign, String key)
	{
		try
		{
			return MD5Signature.verify(verifyData, sign, key);
		}
		catch (Exception e1)
		{
			logger.info("待验签数据:" + verifyData + "(此处省略密钥)，支付宝返回的签名：" + sign);
			e1.printStackTrace();
		}
		return false;
	}

	/**
	 * 对参数进行签名
	 * @param reqParams
	 * @return
	 */
	private String sign(Map<String, String> reqParams, String key)
	{
		String sign = "";
		String signData = ParameterUtil.getSignData(reqParams);
		try
		{
			sign = MD5Signature.sign(signData, key);
		}
		catch (Exception e)
		{
			logger.info("待签名数据：" + signData + "，签名：" + sign);
			e.printStackTrace();
		}
		return sign;
	}
}